home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Environments / PowerMacOberon feb96 / Source / Hex.Mod (.txt) < prev    next >
Encoding:
Oberon Text  |  1994-07-11  |  32.4 KB  |  900 lines  |  [.Ob./.Ob4]

  1. Syntax10b.Scn.Fnt
  2. Syntax10.Scn.Fnt
  3. Syntax10i.Scn.Fnt
  4. MODULE Hex;    (* Hansjoerg Buchser; 25. 2. 1994 *)
  5.     IMPORT Texts, TextFrames, Viewers, Display, Files, Oberon, MenuViewers, Fonts, SYSTEM, Input;
  6.     CONST StandardMenu = "System.Close  System.Copy  System.Grow  Hex.StoreText  Hex.Store ";
  7.         updateByte = 0; changeFont = 1;    (* message id *)
  8.         ord0 = 48; ordA = 65; orda = 97;    (* ASCII values *)
  9.         hexdX = 3; dY = 3;    (* cursor overlapping *)
  10.         begOfLine = 20; barW = 13;    (* x-coords in Frame *)
  11.         colspace = 3; adrlen = 6;    (* number of chars *)
  12.         number = 16;    (* number of bytes per line *)
  13.         DefaultFont = "Courier10.Scn.Fnt";
  14.         MR = 0; MM = 1; ML = 2;
  15.         fgd = Display.white; bgd = Display.black;
  16.     TYPE CursorCoord = POINTER TO CursorCoordDesc;
  17.         CursorCoordDesc = RECORD X, W : INTEGER END;
  18.         Model = POINTER TO ModelDesc;
  19.         ModelDesc = RECORD name : ARRAY 32 OF CHAR; file : Files.File END; 
  20.         Frame = POINTER TO FrameDesc;
  21.         FrameDesc = RECORD (Display.FrameDesc)
  22.             virgin, hasCursor : BOOLEAN;
  23.             cursor1, cursor2 : CursorCoord;    (* primary, secondary cursor *)
  24.             cursorY : INTEGER;
  25.             cursorBytePos : LONGINT;
  26.             model : Model;
  27.             org, len : LONGINT
  28.         END;
  29.         UpdateMsg = RECORD (Display.FrameMsg)
  30.             id : INTEGER;
  31.             file : Files.File;
  32.             pos : LONGINT;
  33.             ch : CHAR
  34.         END;
  35.         CursorMsg = RECORD (Display.FrameMsg)
  36.             pos : LONGINT;
  37.             file : Files.File;
  38.         END;
  39.     VAR font : Fonts.Font;    (* actual font *)
  40.         fontname : ARRAY 32 OF CHAR;    (* name of actual font *)
  41.         fontwidth, fontheight, hmin, hmax, amin, amax : INTEGER;    (* display variables *)
  42.         cursorH, greybar1, greybar2, greybar3 : INTEGER;
  43.         hexcurs, asccurs : CursorCoord;
  44.         nextline : ARRAY number OF CHAR;    (* output variables *)
  45.         R : Files.Rider;
  46.         W : Texts.Writer;
  47.         res : INTEGER;
  48.     (* ____________________________ HexFrames-Part of Module __________________________ *)
  49.     (* ______________________________ some auxiliary functions ____________________________ *)
  50.     PROCEDURE Cap(ch : CHAR) : CHAR;
  51.     BEGIN
  52.         CASE ch OF "a".."z" : RETURN CAP(ch) ELSE RETURN ch END;
  53.     END Cap;
  54.     PROCEDURE DecToHex(d : LONGINT) : CHAR;
  55.     BEGIN
  56.         IF d < 10 THEN d := d + ord0 ELSE d := d + ordA - 10 END;
  57.         RETURN CHR(d)
  58.     END DecToHex;
  59.     PROCEDURE HexToDec(ch : CHAR) : INTEGER;
  60.     BEGIN
  61.         CASE ch OF  "A".."F" : RETURN ORD(ch) - ordA + 10
  62.         | "a".."f" : RETURN ORD(ch) - orda + 10
  63.         | "0".."9" : RETURN ORD(ch) - ord0
  64.         ELSE RETURN -1
  65.         END
  66.     END HexToDec;
  67.     PROCEDURE ReadableChar(ch : CHAR) : CHAR;
  68.     BEGIN
  69.         CASE ORD(ch) OF
  70.             32..126, 128..149, 155 : RETURN ch
  71.             ELSE RETURN "."
  72.         END
  73.     END ReadableChar;
  74.     (* ______________________________ init procedure ____________________________ *) 
  75.     PROCEDURE InitDisplayVars;
  76.         VAR dx, x, y, w, h : INTEGER;
  77.             p : Display.Pattern;
  78.     BEGIN
  79.         Display.GetChar(font.raster, "0", dx, x, y, w, h, p);
  80.         fontwidth := dx;
  81.         fontheight := font.height + 1;
  82.         hmin := begOfLine + (adrlen + colspace)*fontwidth;
  83.         hmax := hmin + (number*3 - 1)*fontwidth;
  84.         amin := hmax + colspace*fontwidth;
  85.         amax := amin + number*fontwidth;
  86.         greybar1 := hmin + (hmax - hmin - fontwidth) DIV 4;
  87.         greybar2 := hmin + (hmax - hmin) DIV 2;
  88.         greybar3 := hmax - (hmax - hmin - fontwidth) DIV 4;
  89.         NEW(hexcurs); hexcurs.W := 2*fontwidth + hexdX;
  90.         NEW(asccurs); asccurs.W := fontwidth;
  91.         cursorH := fontheight
  92.     END InitDisplayVars;
  93.     (* ______________________________ coord conversion ____________________________ *)
  94.     PROCEDURE GetLine(F : Frame; Y : INTEGER; VAR line : INTEGER);
  95.     BEGIN
  96.         IF Y >= F.Y THEN
  97.             line := (F.Y + F.H - Y - dY) DIV fontheight;
  98.             IF (line + 1)*fontheight >= F.H - dY THEN DEC(line) END;
  99.             IF line < 0 THEN line := 0 END
  100.         ELSE
  101.             line :=  (F.H  - dY) DIV fontheight - 1
  102.         END
  103.     END GetLine;
  104.     PROCEDURE GetOffset(F : Frame; X : INTEGER; VAR off : INTEGER);
  105.     BEGIN
  106.         IF (hmin <= X - F.X) & (X - F.X <= hmax) THEN
  107.             off := (X - F.X - hmin + fontwidth DIV 2) DIV (3*fontwidth)
  108.         ELSIF (amin <= X - F.X) & (X - F.X <= amax) THEN
  109.             off := (X - F.X - amin) DIV fontwidth
  110.         ELSE 
  111.             off := -1
  112.         END
  113.     END GetOffset;
  114.     PROCEDURE GetX(F : Frame; pos : LONGINT; VAR hX, aX : INTEGER);
  115.     BEGIN
  116.         IF pos < F.len THEN
  117.             DEC(pos, F.org);
  118.             pos := pos MOD number;
  119.             hX := F.X + hmin + SHORT(pos)*3*fontwidth;
  120.             aX := F.X + amin + SHORT(pos)*fontwidth
  121.         ELSE
  122.             hX := -1; aX := -1
  123.         END
  124.     END GetX;
  125.     PROCEDURE GetY(F : Frame; pos : LONGINT; VAR Y : INTEGER);
  126.     BEGIN
  127.         IF pos < F.len THEN
  128.             DEC(pos, F.org);
  129.             pos := pos DIV number;
  130.             Y := F.Y + F.H - (SHORT(pos) + 1)*fontheight
  131.         ELSE
  132.             Y := -1
  133.         END
  134.     END GetY;
  135.     (* ______________________________ display support ____________________________ *)
  136.     PROCEDURE WriteBang(F : Frame);
  137.         VAR R : Texts.Reader; V : Viewers.Viewer; T : Texts.Text; ch : CHAR;
  138.     BEGIN
  139.         V := Viewers.This(F.X, F.Y);
  140.         IF (V IS MenuViewers.Viewer) & (V.dsc IS TextFrames.Frame) THEN
  141.             T := V.dsc(TextFrames.Frame).text;
  142.             IF T.len > 0 THEN Texts.OpenReader(R, T, T.len - 1); Texts.Read(R, ch) ELSE ch := 0X END;
  143.             IF ch # "!" THEN Texts.Write(W, "!"); Texts.Append(T, W.buf) END
  144.         END
  145.     END WriteBang;
  146.     PROCEDURE DeleteBang(F : Frame);
  147.         VAR R : Texts.Reader; V : Viewers.Viewer; T : Texts.Text; ch : CHAR;
  148.     BEGIN
  149.         V := Viewers.This(F.X, F.Y);
  150.         IF (V IS MenuViewers.Viewer) & (V.dsc IS TextFrames.Frame) THEN
  151.             T := V.dsc(TextFrames.Frame).text;
  152.             IF T.len > 0 THEN Texts.OpenReader(R, T, T.len - 1); Texts.Read(R, ch) ELSE ch := 0X END;
  153.             IF ch = "!" THEN Texts.Delete(T, T.len - 1, T.len) END
  154.         END
  155.     END DeleteBang;
  156.     PROCEDURE InvertCursor(F : Frame);
  157.     BEGIN
  158.         IF (F.X < F.cursor1.X) & (F.cursor1.X + F.cursor1.W < F.X + F.W) &
  159.             (F.Y < F.cursorY) & (F.cursorY + cursorH <= F.Y + F.H) THEN
  160.             F.hasCursor := ~F.hasCursor;
  161.             Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  162.             Display.ReplConst(fgd, F.cursor1.X, F.cursorY, F.cursor1.W, cursorH, Display.invert);
  163.             Display.ReplConstC(F, fgd, F.cursor2.X, F.cursorY, F.cursor2.W, 2, Display.invert)
  164.         END
  165.     END InvertCursor;
  166.     PROCEDURE RemoveCursor(F : Frame);
  167.     BEGIN
  168.         IF F.hasCursor THEN
  169.             InvertCursor(F);
  170.             F.cursorBytePos := -1
  171.         END
  172.     END RemoveCursor;
  173.     PROCEDURE DrawCursor(F : Frame);
  174.     BEGIN
  175.         Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  176.         Display.ReplConstC(F, fgd, F.cursor1.X, F.cursorY, F.cursor1.W, cursorH, Display.invert);
  177.         Display.ReplConstC(F, fgd, F.cursor2.X, F.cursorY, F.cursor2.W, 2,Display.invert)
  178.     END DrawCursor;
  179.     PROCEDURE SetCursor(F : Frame; X, Y : INTEGER);
  180.         VAR offset, line : INTEGER;
  181.             pos : LONGINT;
  182.     BEGIN
  183.         GetOffset(F, X, offset);
  184.         GetLine(F, Y, line);
  185.         pos := LONG(line)*number + offset + F.org;
  186.         IF pos < F.len THEN
  187.             IF F.cursor1 = hexcurs THEN
  188.                 GetX(F, pos, F.cursor1.X, F.cursor2.X);
  189.                 DEC(F.cursor1.X, hexdX DIV 2)
  190.             ELSE (* F.cursor1 = asccurs *)
  191.                 GetX(F, pos, F.cursor2.X, F.cursor1.X);
  192.                 DEC(F.cursor2.X, hexdX DIV 2)
  193.             END;
  194.             GetY(F, pos, F.cursorY);
  195.             DEC(F.cursorY, dY);
  196.             F.cursorBytePos := pos;
  197.             InvertCursor(F)
  198.         END
  199.     END SetCursor;
  200.     (* ______________________________ draw file content ____________________________ *)
  201.     PROCEDURE ShowChar(F : Frame; ch : CHAR; VAR X : INTEGER; Y : INTEGER);
  202.         VAR dx, x, y, w, h : INTEGER;
  203.             p : Display.Pattern;
  204.     BEGIN
  205.         IF (F.X < X) & (X + fontwidth < F.X + F.W) & (F.Y + dY < Y) & (Y + fontheight <= F.Y + F.H) THEN
  206.             Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  207.             Display.GetChar(font.raster, ch, dx, x, y, w, h, p);
  208.             Display.CopyPattern(fgd, p, X+x, Y+y, Display.replace);
  209.             INC(X, dx)
  210.         END
  211.     END ShowChar;
  212.     PROCEDURE ShowSpaces(F : Frame; num : INTEGER; VAR X : INTEGER; Y : INTEGER);
  213.         VAR i : INTEGER;
  214.     BEGIN
  215.         i := 0;
  216.         WHILE i < num  DO 
  217.             ShowChar(F, " ", X, Y);
  218.             INC(i)
  219.         END
  220.     END ShowSpaces;
  221.     PROCEDURE ShowAddress(F : Frame; pos : LONGINT; VAR X : INTEGER; Y : INTEGER);
  222.         VAR div : LONGINT;
  223.     BEGIN
  224.         div := 0100000H;
  225.         REPEAT
  226.             ShowChar(F, DecToHex(pos DIV div), X, Y);
  227.             pos := pos MOD div;
  228.             div :=ASH(div, -4);
  229.         UNTIL div = 0;
  230.     END ShowAddress;
  231.     PROCEDURE ShowHexPart(F : Frame; max : INTEGER; VAR X : INTEGER; Y : INTEGER);
  232.         VAR i : INTEGER;
  233.     BEGIN
  234.         i := 0;
  235.         WHILE i < max DO
  236.             ShowChar(F, DecToHex(ASH(ORD(nextline[i]), -4)), X, Y);
  237.             ShowChar(F, DecToHex(ORD(nextline[i]) MOD 16), X, Y);
  238.             ShowSpaces(F, 1, X, Y);
  239.             INC(i)
  240.         END;
  241.         ShowSpaces(F, (number-i)*3, X, Y)
  242.     END ShowHexPart;
  243.     PROCEDURE ShowAscPart(F : Frame; max : INTEGER; VAR X : INTEGER; Y : INTEGER);
  244.         VAR i : INTEGER;
  245.     BEGIN
  246.         i := 0;
  247.         WHILE i < max DO
  248.             ShowChar(F, ReadableChar(nextline[i]), X, Y);
  249.             INC(i)
  250.         END
  251.     END ShowAscPart;
  252.     PROCEDURE ShowLine(F : Frame; Y, nr : INTEGER; adr : LONGINT);
  253.         VAR X : INTEGER;
  254.     BEGIN
  255.         X := F.X + begOfLine;
  256.         ShowAddress(F, adr, X, Y);
  257.         ShowSpaces(F, colspace, X, Y);
  258.         ShowHexPart(F, nr, X, Y);
  259.         ShowSpaces(F, colspace-1, X, Y);
  260.         ShowAscPart(F, nr, X, Y)
  261.     END ShowLine;
  262.     PROCEDURE DrawGreyBars(F : Frame);
  263.         VAR Y, H, line : INTEGER; help : LONGINT;
  264.     BEGIN
  265.         GetLine(F, F.Y + 1, line);
  266.         help := F.len - F.org;
  267.         IF (line + 1)*number > help THEN (* eof visible *)
  268.             Y := F.Y + F.H - SHORT((help - 1) DIV number + 1)*fontheight - dY;
  269.             H := SHORT((help - 1) DIV number + 1)*fontheight    
  270.         ELSE (* eof not visible *)
  271.             Y := F.Y + F.H - (line + 1)*fontheight - dY;
  272.             H := (line + 1)*fontheight
  273.         END;
  274.         IF (F.H - 1 - dY) DIV fontheight > 0 THEN (* at least one line visible *)    
  275.             Display.ReplPattern(fgd, Display.grey1, F.X + greybar1, Y, 1, H, Display.replace);
  276.             Display.ReplPattern(fgd, Display.grey1, F.X + greybar2, Y, 1, H, Display.replace);
  277.             Display.ReplPattern(fgd, Display.grey1, F.X + greybar3, Y, 1, H, Display.replace)
  278.         END
  279.     END DrawGreyBars;
  280.     PROCEDURE DrawClip(F : Frame);
  281.         CONST clipW = 8; clipH = 2;
  282.         VAR Y : INTEGER;
  283.     BEGIN
  284.         Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  285.         Display.ReplConst(bgd, F.X + 1, F.Y, barW - 2, F.H, Display.replace);
  286.         Y := F.Y + F.H - clipH - SHORT((F.H - clipH)*F.org DIV F.len);
  287.         Display.ReplConst(fgd, F.X + 1, Y, clipW, clipH, Display.replace)
  288.     END DrawClip;
  289.     PROCEDURE Draw(F : Frame; Y, maxY : INTEGER; pos : LONGINT);
  290.         VAR X : INTEGER;
  291.             rest : INTEGER;
  292.     BEGIN
  293.         DEC(Y, fontheight);
  294.         IF F.len > 0 THEN
  295.             Files.Set(R, F.model.file, pos);
  296.             Files.ReadBytes(R, nextline, number);
  297.             WHILE ~R.eof & (Y > maxY) DO
  298.                 ShowLine(F, Y, number, Files.Pos(R) - number);
  299.                 DEC(Y, fontheight);
  300.                 Files.ReadBytes(R, nextline, number)
  301.             END;
  302.             rest := number - SHORT(R.res); 
  303.             IF (Y > maxY) & (rest > 0) THEN
  304.                 ShowLine(F, Y, rest, Files.Pos(R)-rest)
  305.             END;
  306.             DrawClip(F)
  307.         END
  308.     END Draw;
  309.     PROCEDURE DrawFrame(F : Frame);
  310.         VAR line : INTEGER;
  311.     BEGIN
  312.         RemoveCursor(F);
  313.         Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  314.         Display.ReplConst(bgd, F.X, F.Y, F.W, F.H, Display.replace);
  315.         Display.ReplConst(fgd, F.X+barW, F.Y, 1, F.H, Display.replace);
  316.         Draw(F, F.Y + F.H, F.Y + dY, F.org);
  317.         DrawGreyBars(F)
  318.     END DrawFrame;
  319.     (* ______________________________ update procedures ____________________________ *)
  320.     PROCEDURE AscUpdateByte(F : Frame; ch : CHAR);
  321.     BEGIN
  322.         Files.Set(R, F.model.file, F.cursorBytePos);
  323.         Files.Write(R, ch)
  324.     END AscUpdateByte;
  325.     PROCEDURE HexUpdateByte(F : Frame; ord : INTEGER);
  326.         VAR help : CHAR;
  327.     BEGIN
  328.         Files.Set(R, F.model.file, F.cursorBytePos);
  329.         Files.Read(R, help);
  330.         help := CHR(SYSTEM.LSH(ORD(help), 4) + ord);
  331.         Files.Set(R, F.model.file, F.cursorBytePos);
  332.         Files.Write(R, help)
  333.     END HexUpdateByte;
  334.     PROCEDURE Update(F : Frame; pos : LONGINT; ch : CHAR);
  335.         VAR hX, aX, Y : INTEGER;
  336.     BEGIN
  337.         GetX(F, pos, hX, aX);
  338.         GetY(F, pos, Y);
  339.         Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  340.         Display.ReplConstC(F, bgd, hX - hexdX DIV 2, Y - dY, hexcurs.W, cursorH, Display.replace);
  341.         ShowChar(F, DecToHex(ASH(ORD(ch), -4)), hX, Y);
  342.         ShowChar(F, DecToHex(ORD(ch) MOD 16), hX, Y);
  343.         Display.ReplConstC(F, bgd, aX, Y - dY, asccurs.W, cursorH, Display.replace);
  344.         ShowChar(F, ReadableChar(ch), aX, Y)
  345.     END Update;
  346.     PROCEDURE SendUpdateMsg(F : Frame);
  347.         VAR M : UpdateMsg; ch : CHAR;
  348.     BEGIN
  349.         Files.Set(R, F.model.file, F.cursorBytePos);
  350.         Files.Read(R, ch);
  351.         M.id := updateByte; M.file := F.model.file; M.ch := ch; M.pos := F.cursorBytePos;
  352.         Viewers.Broadcast(M)
  353.     END SendUpdateMsg;
  354.     (* ______________________________ scrolling procedures ____________________________ *)
  355.     PROCEDURE ScrollFrame(F : Frame; pos : LONGINT; line : INTEGER);
  356.         VAR H, d, maxline : INTEGER;
  357.     BEGIN
  358.         Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  359.         GetLine(F, F.Y + 1, maxline);
  360.         d := F.H - (maxline + 1)*fontheight;
  361.         IF (F.org < pos) & (pos <= F.org + maxline*number) THEN
  362.             (* scroll down *)
  363.             RemoveCursor(F);
  364.             H := F.H - line*fontheight - d;
  365.             F.org := pos;
  366.             Display.CopyBlock(F.X + barW + 1, F.Y + d - dY, F.W - barW - 1,
  367.                 H, F.X + barW + 1, F.Y + F.H - H - dY, Display.replace);
  368.             Display.ReplConst(bgd, F.X + barW + 1, F.Y, F.W - barW - 1, F.H - H - dY, Display.replace);
  369.             Draw(F, F.Y + F.H - H, F.Y + dY, F.org + (maxline - line + 1)*number);
  370.             DrawGreyBars(F)
  371.         ELSIF (pos < F.org) & (F.org <= pos + maxline*number) THEN
  372.             (* scroll up *)
  373.             RemoveCursor(F);
  374.             IF F.len DIV number <= maxline THEN (* whole file fits in frame *)
  375.                 d := F.H - SHORT(F.len DIV number + 1)*fontheight
  376.             END;
  377.             H := (line + 1)*fontheight;
  378.             F.org := pos;
  379.             Display.CopyBlock(F.X + barW + 1, F.Y + F.H - H - dY,
  380.                 F.W - barW - 1, H, F.X + barW + 1, F.Y + d - dY, Display.replace);
  381.             Display.ReplConst(bgd, F.X + barW + 1, F.Y + H + d - dY, F.W - barW - 1, F.H - H - d + dY, Display.replace);
  382.             Draw(F, F.Y + F.H, F.Y + H + d - 1, F.org);
  383.             DrawGreyBars(F)
  384.         ELSE
  385.             (* redraw whole frame *)
  386.             F.org := pos;
  387.             DrawFrame(F)
  388.         END
  389.     END ScrollFrame;
  390.     PROCEDURE Scroll(F : Frame; X, Y : INTEGER; keysum : SET);
  391.     VAR pos : LONGINT;
  392.         line, line1, Ybar : INTEGER;
  393.         PROCEDURE Underscore(col, mode : INTEGER);
  394.         BEGIN
  395.             Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  396.             Display.ReplConstC(F, col, F.X + begOfLine, Ybar - 3, adrlen*fontwidth, 2, mode)
  397.         END Underscore;
  398.         PROCEDURE Track(VAR X, Y : INTEGER; VAR keysum : SET);
  399.             VAR keys, prim : SET; Y1, oldline : INTEGER;
  400.         BEGIN
  401.             keys := keysum; prim := keysum;
  402.             oldline := -1; Ybar := -1;
  403.             WHILE keys # {} DO
  404.                 Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y);
  405.                 GetLine(F, Y, line);
  406.                 IF line*number + F.org >= F.len THEN
  407.                     line := SHORT((F.len - F.org - 1) DIV number)
  408.                 END;
  409.                  IF line # oldline THEN
  410.                     IF ~(MM IN prim) THEN Underscore(bgd, Display.replace) END;
  411.                     GetY(F, line*number + F.org, Ybar);
  412.                     IF ~(MM IN prim) THEN Underscore(fgd, Display.replace) END;
  413.                     oldline := line
  414.                 END;
  415.                 Input.Mouse(keys, X, Y);
  416.                 keysum := keysum + keys
  417.             END
  418.         END Track;
  419.     BEGIN
  420.         pos := F.org;
  421.         IF MR IN keysum THEN
  422.             Track(X, Y, keysum);
  423.             IF keysum = {ML, MM, MR} THEN
  424.                 (* cancel *)
  425.                 Underscore(bgd, Display.replace);
  426.                 RETURN
  427.             ELSE
  428.                 (* this line to bottom of frame *)
  429.                 GetLine(F, F.Y + 1, line1);
  430.                 pos := F.org - (line1 - line)*number;
  431.                 IF pos < 0  THEN
  432.                     IF F.len DIV number > line1 THEN (* whole file fist in frame *)
  433.                         line := ((line1 + 1)*number - SHORT(F.org)) DIV number - 1
  434.                     END;
  435.                     pos := 0
  436.                 END;
  437.                 Underscore(bgd, Display.replace)
  438.             END
  439.         ELSIF MM IN keysum THEN
  440.             Track(X, Y, keysum);
  441.             IF keysum = {ML, MM, MR} THEN
  442.                 (* cancel *)
  443.                 RETURN
  444.             ELSIF MR IN keysum THEN
  445.                 (* scroll to bof *)
  446.                 pos := 0;
  447.                 IF pos # F.org THEN F.org := pos; DrawFrame(F); RETURN END
  448.             ELSIF ML IN keysum THEN
  449.                 (* scroll to eof *)
  450.                 pos := (F.len DIV number - 2)*number (* 2 is heuristic *);
  451.                 IF pos < 0 THEN pos := 0 END;
  452.                 IF pos # F.org THEN F.org := pos; DrawFrame(F); RETURN END
  453.             ELSE
  454.                 (* set clip to position *)
  455.                 pos := (F.Y + F.H - Y)*F.len DIV F.H;
  456.                 pos := pos DIV number*number;
  457.                 line := SHORT(pos - F.org) DIV number;
  458.                 IF line < 0 THEN (* scroll up *)
  459.                     GetLine(F, F.Y + 1, line1);
  460.                     IF F.len DIV number > line1 THEN
  461.                         line := line1 + line
  462.                     ELSE (* whole file fits in frame *)
  463.                         line := SHORT(F.len) DIV number + line
  464.                     END
  465.                 END
  466.             END
  467.         ELSIF ML IN keysum THEN
  468.             Track(X, Y, keysum);
  469.             IF keysum = {ML, MM, MR} THEN
  470.                 (* cancel *)
  471.                 Underscore(bgd, Display.replace);
  472.                 RETURN
  473.             ELSE
  474.                 (* this line to top of frame *)
  475.                 pos := line*number + F.org;
  476.                 IF pos > F.len THEN pos := F.len DIV number*number END;
  477.                 Underscore(bgd, Display.replace)
  478.             END
  479.         END;
  480.         IF F.org # pos THEN ScrollFrame(F, pos, line) END
  481.     END Scroll;
  482.     (* ______________________________ mouse tracking ____________________________ *)
  483.     PROCEDURE TrackMouse(F : Frame; X, Y : INTEGER; VAR keys : SET);
  484.         VAR off, line : INTEGER;
  485.             track : BOOLEAN;
  486.             prim, sec : CursorCoord;
  487.     BEGIN
  488.         IF ~F.hasCursor & (keys = {ML}) THEN
  489.             Oberon.PassFocus(Viewers.This(X, Y));
  490.             track := TRUE
  491.         ELSIF keys = {ML} THEN
  492.             track := TRUE
  493.         ELSE
  494.             track := FALSE
  495.         END;
  496.         WHILE keys # {} DO
  497.             Input.Mouse(keys, X, Y);
  498.             IF (F.X + hmin < X) & (X < F.X + hmax) THEN
  499.                 prim := hexcurs; sec := asccurs;
  500.             ELSIF (F.X + amin < X) & (X < F.X + amax) THEN
  501.                 prim := asccurs; sec := hexcurs
  502.             ELSE
  503.                 RemoveCursor(F); prim := NIL; sec := NIL;
  504.             END;
  505.             GetLine(F, Y, line); GetOffset(F, X, off);
  506.             IF track THEN
  507.                 IF (prim # NIL) & ((F.cursor1 # prim) OR (F.org + line*number + off # F.cursorBytePos)) THEN
  508.                     RemoveCursor(F);
  509.                     F.cursor1 := prim; F.cursor2 := sec;
  510.                     SetCursor(F, X, Y)
  511.                 END
  512.             END;
  513.             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y)
  514.         END
  515.     END TrackMouse;
  516.     (* ______________________________ edit procedures ____________________________ *)
  517.     PROCEDURE CopyFile(F : Frame);
  518.         CONST bufSize = 512;
  519.         VAR new : Files.File;
  520.             writer : Files.Rider;
  521.             buf : ARRAY bufSize OF CHAR;
  522.     BEGIN
  523.         Files.Set(R, F.model.file, 0);
  524.         new := Files.New(F.model.name);
  525.         Files.Set(writer, new, 0);
  526.         Files.ReadBytes(R, buf, bufSize);
  527.         WHILE ~R.eof DO
  528.             Files.WriteBytes(writer, buf, bufSize);
  529.             Files.ReadBytes(R, buf, bufSize)
  530.         END;
  531.         Files.WriteBytes(writer, buf, bufSize - R.res);
  532.         F.model.file := new
  533.     END CopyFile;
  534.     PROCEDURE Edit(F : Frame; ch : CHAR);
  535.         CONST cright = 0C3X; cleft = 0C4X;
  536.         VAR hX, aX, Y : INTEGER;
  537.     BEGIN
  538.         IF F.hasCursor THEN
  539.             IF (ch = cright) & (F.cursorBytePos # F.len-1) THEN
  540.                 InvertCursor(F);
  541.                 INC(F.cursorBytePos);
  542.                 GetX(F, F.cursorBytePos, hX, aX); GetY(F, F.cursorBytePos, Y);
  543.                 IF F.cursor1 = hexcurs THEN SetCursor(F, hX, Y) ELSE SetCursor(F, aX, Y) END
  544.             ELSIF (ch = cleft) & (F.cursorBytePos # 0) THEN
  545.                 InvertCursor(F);
  546.                 DEC(F.cursorBytePos);
  547.                 GetX(F, F.cursorBytePos, hX, aX); GetY(F, F.cursorBytePos, Y);
  548.                 IF F.cursor1 = hexcurs THEN SetCursor(F, hX, Y) ELSE SetCursor(F, aX, Y) END
  549.             ELSIF F.cursor1 = hexcurs THEN
  550.                 IF HexToDec(ch) >= 0 THEN
  551.                     IF F.virgin THEN F.virgin := FALSE; CopyFile(F) END;
  552.                     HexUpdateByte(F, HexToDec(ch));
  553.                     SendUpdateMsg(F);
  554.                     DrawCursor(F)
  555.                 END
  556.             ELSIF F.cursor1 = asccurs THEN
  557.                 IF (ch = ".") OR (ReadableChar(ch) # ".") THEN
  558.                     IF F.virgin THEN F.virgin := FALSE; CopyFile(F) END;
  559.                     AscUpdateByte(F, ch);
  560.                     SendUpdateMsg(F);
  561.                     DrawCursor(F);
  562.                     IF F.cursorBytePos # F.len-1 THEN
  563.                         InvertCursor(F);
  564.                         INC(F.cursorBytePos);
  565.                         GetX(F, F.cursorBytePos, hX, aX); GetY(F, F.cursorBytePos, Y);
  566.                         SetCursor(F, aX, Y)
  567.                     END
  568.                 END
  569.             END
  570.         END
  571.     END Edit;
  572.     (* ______________________________ message handling ____________________________ *) 
  573.     PROCEDURE Copy(src, dst : Frame);
  574.     BEGIN
  575.         dst.virgin := src.virgin; dst.hasCursor := FALSE;
  576.         dst.cursor1 := NIL; dst.cursor2 := NIL; dst.cursorBytePos := -1;
  577.         NEW(dst.model);  dst.model := src.model;
  578.         dst.org := src.org; dst.len := src.len;
  579.         dst.handle := src.handle
  580.     END Copy;
  581.     PROCEDURE Modify(F : Frame; Y, H : INTEGER);
  582.         VAR line, dH : INTEGER;
  583.     BEGIN
  584.         dH := H - F.H;
  585.         IF dH > 0 THEN (* extend *)
  586.             Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  587.             GetLine(F, F.Y, line);
  588.             IF F.Y + F.H # Y + H THEN
  589.                 Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y + dH, Display.replace)
  590.             END;
  591.             F.Y := Y; F.H := H;
  592.             Display.ReplConst(bgd, F.X, F.Y, F.W, dH, Display.replace);
  593.             Display.ReplConst(fgd, F.X + barW, F.Y, 1, dH, Display.replace);
  594.             Draw(F, Y + H - line*fontheight, F.Y + dY, F.org + line*number);
  595.             DrawGreyBars(F)
  596.         ELSIF dH < 0 THEN (* reduce *)
  597.             Oberon.RemoveMarks(F.X, F.Y, F.W, F.H);
  598.             line := (H -1- dY) DIV fontheight;
  599.             IF (line + 1)*fontheight >= H - dY THEN DEC(line) END;
  600.             dH := (line + 1)*fontheight;
  601.             IF F.Y + F.H # Y + H THEN
  602.                 Display.CopyBlock(F.X, F.Y + F.H - dH - dY, F.W, dH + dY, F.X, Y + H - dH - dY, Display.replace)
  603.             END;
  604.             F.Y := Y; F.H := H;
  605.             IF dH < 0 THEN dH := 0 END;
  606.             Display.ReplConst(bgd, F.X + barW + 1, F.Y, F.W - barW - 1, H - dH - dY, Display.replace);
  607.             DrawClip(F);
  608.             DrawGreyBars(F)
  609.         END
  610.     END Modify;
  611.     PROCEDURE Handle(F : Display.Frame; VAR M : Display.FrameMsg);
  612.         VAR dest : Frame;
  613.     BEGIN
  614.         WITH F : Frame DO
  615.             IF M IS Oberon.InputMsg THEN
  616.                 WITH M : Oberon.InputMsg DO
  617.                     IF M.id = Oberon.track THEN
  618.                         IF M.X < F.X + barW THEN
  619.                             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y);
  620.                             Scroll(F, M.X, M.Y, M.keys)
  621.                         ELSE
  622.                             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y);
  623.                             TrackMouse(F, M.X, M.Y, M.keys)
  624.                         END
  625.                     ELSIF M.id = Oberon.consume THEN
  626.                         Edit(F, M.ch)
  627.                     END
  628.                 END
  629.             ELSIF M IS MenuViewers.ModifyMsg THEN
  630.                 WITH M : MenuViewers.ModifyMsg DO
  631.                     RemoveCursor(F);
  632.                     Modify(F, M.Y, M.H)
  633.                 END
  634.             ELSIF M IS Oberon.CopyMsg THEN
  635.                 WITH M : Oberon.CopyMsg DO
  636.                     IF M.F = NIL THEN NEW(dest); M.F := dest END;
  637.                     RemoveCursor(F);
  638.                     Copy(F, M.F(Frame))
  639.                 END
  640.             ELSIF M IS UpdateMsg THEN
  641.                 WITH M : UpdateMsg DO
  642.                     IF M.id = changeFont THEN
  643.                         DrawFrame(F)
  644.                     ELSIF M.id = updateByte THEN
  645.                         IF M.file = F.model.file THEN
  646. nScanner(S, par.text, par.pos)
  647.         END;
  648.         Texts.Scan(S);
  649.         IF (S.class = Texts.Char) & (S.c = "^") THEN
  650.             Oberon.GetSelection(T, beg, end, time);
  651.             IF time > 0 THEN Texts.OpenScanner(S, T, beg); Texts.Scan(S) END
  652.         END;
  653.         IF (S.class = Texts.Name) & (V.dsc # NIL) & (V.dsc.next IS Frame) THEN
  654.             F := V.dsc.next(Frame);
  655.             COPY(S.s, name)
  656.         ELSE
  657.             F := NIL
  658.         END
  659.     END FindStoreFrame;
  660.     PROCEDURE FindInputName(VAR name : ARRAY OF CHAR);
  661.         VAR T : Texts.Text;
  662.             S : Texts.Scanner;
  663.             beg, end, time : LONGINT;
  664.     BEGIN
  665.         Texts.OpenScanner(S, Oberon.Par.text, Oberon.Par.pos);
  666.         Texts.Scan(S);
  667.         IF (S.class = Texts.Char) & (S.c = "^") OR (S.line # 0) THEN
  668.             Oberon.GetSelection(T, beg, end, time);
  669.             IF time >= 0 THEN
  670.                 Texts.OpenScanner(S, T, beg);
  671.                 Texts.Scan(S)
  672.             END
  673.         END;
  674.         IF S.class = Texts.Name THEN
  675.             COPY(S.s, name)
  676.         ELSE
  677.             COPY("", name)
  678.         END
  679.     END FindInputName;
  680.     PROCEDURE FontLogText(name : ARRAY OF CHAR; res : INTEGER);
  681.     BEGIN
  682.         Texts.WriteString(W, name);
  683.         IF res = 1 THEN (* not found *)
  684.             Texts.WriteString(W, " not found");
  685.         ELSIF res = 2 THEN (* not a non-proportional font *)
  686.             Texts.WriteString(W, " is not a fixed-width font")
  687.         END;
  688.         Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf)
  689.     END FontLogText;
  690.     PROCEDURE SetRider(VAR done : BOOLEAN);
  691.         VAR M : CursorMsg;
  692.     BEGIN
  693.         M.pos := -1;
  694.         Viewers.Broadcast(M);
  695.         IF M.pos >= 0 THEN
  696.             Files.Set(R, M.file, M.pos); done := TRUE
  697.         ELSE
  698.             done := FALSE
  699.         END
  700.     END SetRider;
  701.     (* ______________________________ Commands of Module Hex ____________________________ *)
  702.     PROCEDURE Open*;
  703.         VAR F : Frame;
  704.             V : Viewers.Viewer;
  705.             File : Files.File;
  706.             X, Y : INTEGER;
  707.             name : ARRAY 32 OF CHAR;
  708.             res : INTEGER;
  709.     BEGIN
  710.         FindInputName(name);
  711.         IF name # "" THEN
  712.             File := Files.Old(name);
  713.             IF File # NIL THEN
  714.                 IF fontname # "" THEN
  715.                     NEW(F);
  716.                     OpenNewFrame(F, File, name, Handle);
  717.                     Oberon.AllocateUserViewer(Oberon.Mouse.X, X, Y);
  718.                     V := MenuViewers.New(TextFrames.NewMenu(name,
  719.                             StandardMenu), F, TextFrames.menuH, X, Y)
  720.                 ELSE
  721.                     Texts.WriteString(W, "invalid font"); Texts.WriteLn(W);
  722.                     Texts.Append(Oberon.Log, W.buf)
  723.                 END
  724.             ELSE
  725.                 Texts.WriteString(W, "file not found"); Texts.WriteLn(W);
  726.                 Texts.Append(Oberon.Log, W.buf)
  727.             END
  728.         END
  729.     END Open;
  730.     PROCEDURE Store*;
  731.         VAR F : Frame;
  732.             name : ARRAY 32 OF CHAR;
  733.         PROCEDURE Backup(VAR name : ARRAY OF CHAR);
  734.             VAR res, i : INTEGER;
  735.                 bak : ARRAY 32 OF CHAR;
  736.         BEGIN
  737.             i := 0;
  738.             WHILE name[i] # 0X DO bak[i] := name[i]; INC(i) END;
  739.             bak[i] := "."; bak[i+1] := "B"; bak[i+2] := "a"; bak[i+3] := "k";
  740.             bak[i+4] := 0X;
  741.             Files.Rename(name, bak, res);
  742.         END Backup;
  743.     BEGIN
  744.         Texts.WriteString(W, "Hex.Store ");
  745.         FindStoreFrame(F, name);
  746.         IF F # NIL THEN
  747.             Texts.WriteString(W, name);
  748.             Texts.WriteLn(W);
  749.             Texts.Append(Oberon.Log, W.buf);
  750.             Backup(name);
  751.             StoreFile(F, name)
  752.         END
  753.     END Store;
  754.     PROCEDURE StoreText*;
  755.         VAR F : Frame;
  756.             name : ARRAY 32 OF CHAR;
  757.         PROCEDURE NewName(VAR name : ARRAY OF CHAR);
  758.             VAR i : INTEGER;
  759.         BEGIN
  760.             i := 0;
  761.             WHILE name[i] # 0X DO INC(i) END;
  762.             name[i] := "."; name[i+1] := "T"; name[i+2] := "e"; name[i+3] := "x"; name[i+4] := "t";
  763.             name[i+5] := 0X
  764.         END NewName;
  765.     BEGIN
  766.         Texts.WriteString(W, "Hex.StoreText ");
  767.         FindStoreFrame(F, name);
  768.         IF F # NIL THEN
  769.             NewName(name);
  770.             Texts.WriteString(W, name);
  771.             Texts.WriteLn(W);
  772.             Texts.Append(Oberon.Log, W.buf);
  773.             StoreTextToFile(F, name)
  774.         END
  775.     END StoreText;
  776.     PROCEDURE SetFont*;
  777.         VAR res : INTEGER;
  778.             name : ARRAY 32 OF CHAR;
  779.     BEGIN
  780.         FindInputName(name);
  781.         IF name # "" THEN
  782.             ChangeFont(name, res);
  783.             IF res # 0 THEN
  784.                 FontLogText(name, res)
  785.             END
  786.         END
  787.     END SetFont;
  788.     PROCEDURE GetSInt*;
  789.         VAR x : CHAR; done : BOOLEAN;
  790.     BEGIN
  791.         SetRider(done);
  792.         IF done THEN
  793.             Files.Read(R, x);
  794.             Texts.WriteString(W, "SHORTINT :"); Texts.Write(W, 09X);
  795.             Texts.WriteInt(W, ORD(x), 0); Texts.WriteLn(W);
  796.             Texts.Append(Oberon.Log, W.buf)
  797.         END;
  798.     END GetSInt;
  799.     PROCEDURE GetInt*;
  800.         VAR x : INTEGER; done : BOOLEAN;
  801.     BEGIN
  802.         SetRider(done);
  803.         IF done THEN
  804.             Files.ReadInt(R, x);
  805.             Texts.WriteString(W, "INTEGER :"); Texts.Write(W, 09X);
  806.             Texts.WriteInt(W, x, 0); Texts.WriteLn(W);
  807.             Texts.Append(Oberon.Log, W.buf)
  808.         END
  809.     END GetInt;
  810.     PROCEDURE GetLInt*;
  811.         VAR x : LONGINT; done : BOOLEAN;
  812.     BEGIN
  813.         SetRider(done);
  814.         IF done THEN
  815.             Files.ReadLInt(R, x);
  816.             Texts.WriteString(W, "LONGINT :"); Texts.Write(W, 09X);
  817.             Texts.WriteInt(W, x, 0); Texts.WriteLn(W);
  818.             Texts.Append(Oberon.Log, W.buf)
  819.         END
  820.     END GetLInt;
  821.     PROCEDURE GetReal*;
  822.         VAR x : REAL; done : BOOLEAN;
  823.     BEGIN
  824.         SetRider(done);
  825.         IF done THEN
  826.             Files.ReadReal(R, x);
  827.             Texts.WriteString(W, "REAL :"); Texts.Write(W, 09X);
  828.             Texts.WriteReal(W, x, 20); Texts.WriteLn(W);
  829.             Texts.Append(Oberon.Log, W.buf)
  830.         END
  831.     END GetReal;
  832.     PROCEDURE GetLReal*;
  833.         VAR x : LONGREAL; done : BOOLEAN;
  834.     BEGIN
  835.         SetRider(done);
  836.         IF done THEN
  837.             Files.ReadLReal(R, x);
  838.             Texts.WriteString(W, "LONGREAL :"); Texts.Write(W, 09X);
  839.             Texts.WriteLongReal(W, x, 20); Texts.WriteLn(W);
  840.             Texts.Append(Oberon.Log, W.buf)
  841.         END
  842.     END GetLReal;
  843.     PROCEDURE GetNum*;
  844.         VAR x, n : LONGINT; done : BOOLEAN;
  845.     BEGIN
  846.         SetRider(done);
  847.         IF done THEN
  848.             n := Files.Pos(R);
  849.             Files.ReadNum(R, x);
  850.             n := Files.Pos(R) - n;
  851.             Texts.WriteString(W, "Number ["); Texts.WriteInt(W, n, 0);
  852.             Texts.WriteString(W, " Byte(s)] :"); Texts.Write(W, 09X);
  853.             Texts.WriteInt(W, x, 0); Texts.WriteLn(W);
  854.             Texts.Append(Oberon.Log, W.buf)
  855.         END
  856.     END GetNum;
  857.     PROCEDURE GetSet*;
  858.         VAR x : SET; done : BOOLEAN; i, last : SHORTINT;
  859.     BEGIN
  860.         SetRider(done);
  861.         IF done THEN
  862.             Files.ReadSet(R, x);
  863.             Texts.WriteString(W, "SET :"); Texts.Write(W, 09X); Texts.Write(W, "{");
  864.             i := 0; last := -1; 
  865.             REPEAT
  866.                 IF i IN x THEN
  867.                     IF last >= 0 THEN Texts.WriteInt(W, last, 0); Texts.Write(W, ",") END;
  868.                     last := i;
  869.                 END;
  870.                 INC(i)
  871.             UNTIL (i = 32);
  872.             IF last >= 0 THEN Texts.WriteInt(W, last, 0) END;
  873.             Texts.Write(W, "}");
  874.             Texts.WriteLn(W);
  875.             Texts.Append(Oberon.Log, W.buf)
  876.         END
  877.     END GetSet;
  878.     PROCEDURE GetBool*;
  879.         VAR x : CHAR; done : BOOLEAN;
  880.     BEGIN
  881.         SetRider(done);
  882.         IF done THEN
  883.             Files.Read(R, x);
  884.             Texts.WriteString(W, "BOOLEAN :"); Texts.Write(W, 09X);
  885.             IF x = 01X THEN Texts.WriteString(W, "TRUE")
  886.             ELSE Texts.WriteString(W, "FALSE")
  887.             END;
  888.             Texts.WriteLn(W);
  889.             Texts.Append(Oberon.Log, W.buf)
  890.         END
  891.     END GetBool;
  892. BEGIN
  893.     Texts.OpenWriter(W);
  894.     COPY(DefaultFont, fontname);
  895.     ChangeFont(fontname, res);
  896.     IF res # 0 THEN
  897.         FontLogText(fontname, res);
  898.         COPY("", fontname)
  899. END Hex.
  900.